﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;
using System.IO;
using System.Web;
using WxSDK.Config;
using System.IO.Compression;
using System.Collections;
using System.Reflection;

namespace WxSDK.Common
{
	/// <summary>
	/// http连接基础类，负责底层的http通信
	/// </summary>
	public class HttpService
	{
		/// <summary>
		/// 处理http Post请求，返回数据
		/// </summary>
		/// <param name="xml">请求时的参数（XML格式）</param>
		/// <param name="url">请求的url地址</param>
		/// <param name="isUseCert">是否使用证书</param>
		/// <param name="timeout">请求超时时间（秒）</param>
		/// <returns></returns>
		public static string Post(string xml, string url, bool isUseCert, int timeout)
		{
			System.GC.Collect();//垃圾回收，回收没有正常关闭的http连接

			string result = "";//返回结果

			HttpWebRequest request = null;
			HttpWebResponse response = null;
			Stream reqStream = null;

			try
			{
				//设置最大连接数
				ServicePointManager.DefaultConnectionLimit = 200;
				//设置https验证方式
				if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
				{
					ServicePointManager.ServerCertificateValidationCallback =
							new RemoteCertificateValidationCallback(CheckValidationResult);
				}

				/***************************************************************
				* 下面设置HttpWebRequest的相关属性
				* ************************************************************/
				request = (HttpWebRequest)WebRequest.Create(url);

				request.Method = "POST";
				request.Timeout = timeout * 1000;

				//设置代理服务器
				if (!string.IsNullOrEmpty(WxConfig.PROXY_URL) && WxConfig.PROXY_URL != "0.0.0.0:0")
				{
					WebProxy proxy = new WebProxy();                          //定义一个网关对象
					proxy.Address = new Uri(WxConfig.PROXY_URL);              //网关服务器端口:端口
					request.Proxy = proxy;
				}
				//设置POST的数据类型和长度
				request.ContentType = "text/xml";
				byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
				request.ContentLength = data.Length;

				//是否使用证书
				if (isUseCert)
				{
					string path = HttpContext.Current.Request.PhysicalApplicationPath;
					X509Certificate2 cert = new X509Certificate2(path + WxConfig.SSLCERT_PATH, WxConfig.SSLCERT_PASSWORD, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
					request.ClientCertificates.Add(cert);
					Log.Debug("WxPayApi", "Post Xml数据时，使用了证书！");
				}

				//往服务器写入数据
				reqStream = request.GetRequestStream();
				reqStream.Write(data, 0, data.Length);
				reqStream.Close();

				//获取服务端返回
				response = (HttpWebResponse)request.GetResponse();

				//获取服务端返回数据
				StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
				result = sr.ReadToEnd().Trim();
				sr.Close();
			}
			catch (System.Threading.ThreadAbortException e)
			{
				Log.Error("HttpService", "http通信时引发的异常，具体内容如下.");
				Log.Error("异常消息: {0}", e.Message);
				System.Threading.Thread.ResetAbort();
			}
			catch (WebException e)
			{
				Log.Error("HttpService", e.ToString());
				if (e.Status == WebExceptionStatus.ProtocolError)
				{
					Log.Error("HttpService", "Response : " + StreamToString(((HttpWebResponse)e.Response).GetResponseStream()));
					Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
					Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
				}
				throw new WxException(e.ToString());
			}
			catch (Exception e)
			{
				Log.Error("HttpService", e.ToString());
				throw new WxException(e.ToString());
			}
			finally
			{
				//关闭连接和流
				if (response != null)
				{
					response.Close();
				}
				if (request != null)
				{
					request.Abort();
				}
			}
			return result;
		}
		/// <summary>
		/// 处理http GET请求，返回数据
		/// </summary>
		/// <param name="url">请求的url地址</param>
		/// <returns>http GET成功后返回的数据，失败抛WebException异常</returns>
		public static string Get(string url)
		{
			System.GC.Collect();
			string result = "";

			HttpWebRequest request = null;
			HttpWebResponse response = null;

			//请求url以获取数据
			try
			{
				//设置最大连接数
				ServicePointManager.DefaultConnectionLimit = 200;
				//设置https验证方式
				if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
				{
					ServicePointManager.ServerCertificateValidationCallback =
							new RemoteCertificateValidationCallback(CheckValidationResult);
				}

				/***************************************************************
				* 下面设置HttpWebRequest的相关属性
				* ************************************************************/
				request = (HttpWebRequest)WebRequest.Create(url);
				request.Method = "GET";

				//设置代理
				if (!string.IsNullOrEmpty(WxConfig.PROXY_URL) && WxConfig.PROXY_URL != "0.0.0.0:0")
				{
					WebProxy proxy = new WebProxy();
					proxy.Address = new Uri(WxConfig.PROXY_URL);
					request.Proxy = proxy;
				}
				//获取服务器返回
				response = (HttpWebResponse)request.GetResponse();

				//获取HTTP返回数据
				StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
				result = sr.ReadToEnd().Trim();
				sr.Close();
			}
			catch (System.Threading.ThreadAbortException e)
			{
				Log.Error("HttpService", "http通信时引发的异常，具体内容如下.");
				Log.Error("异常消息: {0}", e.Message);
				System.Threading.Thread.ResetAbort();
			}
			catch (WebException e)
			{
				Log.Error("HttpService", e.ToString());
				if (e.Status == WebExceptionStatus.ProtocolError)
				{
					Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
					Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
				}
				throw new WxException(e.ToString());
			}
			catch (Exception e)
			{
				Log.Error("HttpService", e.ToString());
				throw new WxException(e.ToString());
			}
			finally
			{
				//关闭连接和流
				if (response != null)
				{
					response.Close();
				}
				if (request != null)
				{
					request.Abort();
				}
			}
			return result;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="certificate"></param>
		/// <param name="chain"></param>
		/// <param name="errors"></param>
		/// <returns></returns>
		public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
		{
			//直接确认，否则打不开    
			return true;
		}

		#region Http请求相关方法
		/// <summary>
		/// http post 请求方法，返回HttpWebResponse对像，请手动关闭。
		/// </summary>
		/// <param name="postUrl">请求地址（例：http://xxx.com/acquireData）</param>
		/// <param name="param">请求参数（例：phone=12345678901）</param>
		/// <param name="ContentType">application/x-www-form-urlencoded或application/json</param>
		/// <param name="isGzip">是否启用GZIP压缩</param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <param name="isUseCert">是否使用证书</param>
		/// <param name="timeout">请求超时时间（秒）</param>
		/// <returns></returns>
		public static HttpWebResponse HttpPostResultResponse(string postUrl, string param, string ContentType = "application/x-www-form-urlencoded", bool isGzip = false, Hashtable hasHeaders = null, bool isUseCert = false, int timeout = 30)
		{
			//垃圾回收，回收没有正常关闭的http连接
			System.GC.Collect();
			bool flag = true;
			HttpWebRequest request = null;
			HttpWebResponse response = null;
			try
			{
				//设置最大连接数
				ServicePointManager.DefaultConnectionLimit = 200;
				//设置验证服务器证书的回调
				if (postUrl.StartsWith("https", StringComparison.OrdinalIgnoreCase))
				{
					ServicePointManager.ServerCertificateValidationCallback =
							new RemoteCertificateValidationCallback(CheckValidationResult);
				}
				//根据是否启用GZIP压缩,得到POST的数据类型和长度
				byte[] bytes = Encoding.UTF8.GetBytes(param);
				byte[] byteParam;
				if (isGzip)
				{
					MemoryStream ms = new MemoryStream();
					GZipStream compressedzipStream = new GZipStream(ms, CompressionMode.Compress, true);
					compressedzipStream.Write(bytes, 0, bytes.Length);
					compressedzipStream.Close();
					byteParam = ms.ToArray();
				}
				else
				{
					byteParam = bytes;
				}

				request = (HttpWebRequest)WebRequest.Create(postUrl);
				request.Timeout = timeout * 1000;
				request.Method = "POST";
				request.ContentLength = byteParam.Length;

				//设置代理服务器
				if (!string.IsNullOrEmpty(WxConfig.PROXY_URL) && WxConfig.PROXY_URL != "0.0.0.0:0")
				{
					WebProxy proxy = new WebProxy();                          //定义一个网关对象
					proxy.Address = new Uri(WxConfig.PROXY_URL);              //网关服务器端口:端口
					request.Proxy = proxy;
				}
				//是否使用证书
				if (isUseCert)
				{
					string path = HttpContext.Current.Request.PhysicalApplicationPath;
					X509Certificate2 cert = new X509Certificate2(path + WxConfig.SSLCERT_PATH, WxConfig.SSLCERT_PASSWORD, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
					request.ClientCertificates.Add(cert);
					Log.Debug("WxPayApi", "Post数据时，使用了证书！");
				}
				//设置POST的数据格式
				request.ContentType = ContentType;
				if (ContentType == "application/json")
				{
					request.Accept = ContentType;
				}
				//设置UserAgent请求头(微信支付APIV3的请求，必须带有此标头)
				string UserAgent = $"OS/{Environment.OSVersion.Platform} WxSDK/{Assembly.GetExecutingAssembly().GetName().Version}";
				request.UserAgent = UserAgent;
				//添加请求头中的键值对
				if (hasHeaders != null)
				{
					foreach (DictionaryEntry de in hasHeaders)
					{
						request.Headers.Remove(de.Key.ToString());
						request.Headers.Add(de.Key.ToString(), de.Value.ToString());
					}
				}

				//往请求流对像中写入数据
				Stream requestStream = request.GetRequestStream();
				requestStream.Write(byteParam, 0, byteParam.Length);
				requestStream.Close();

				//获取服务端返回
				response = (HttpWebResponse)request.GetResponse();
			}
			catch (System.Threading.ThreadAbortException e)
			{
				flag = false;
				Log.Error("HttpService", "http通信时引发的异常，具体内容如下.");
				Log.Error("异常消息: {0}", e.Message);
				System.Threading.Thread.ResetAbort();
			}
			catch (WebException e)
			{
				Log.Error("HttpService", e.ToString());
				if (e.Status == WebExceptionStatus.ProtocolError)
				{
					string result = StreamToString(((HttpWebResponse)e.Response).GetResponseStream());
					Log.Error("HttpService", "Response : " + result);
					Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
					Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
					if (string.IsNullOrEmpty(result))
					{
						flag = false;
						throw new WxException(e.ToString());
					}
				}
				else
				{
					flag = false;
					throw new WxException(e.ToString());
				}
			}
			catch (Exception e)
			{
				flag = false;
				Log.Error("HttpService", e.ToString());
				throw new WxException(e.ToString());
			}
			finally
			{
				//关闭连接和流
				if (request != null)
				{
					request.Abort();
				}
				if (!flag && response != null)
				{
					response.Close();
				}
			}
			return response;
		}
		/// <summary>
		/// http post 请求方法，请求参数格式为JSON，返回HttpWebResponse对像，请手动关闭。
		/// </summary>
		/// <param name="postUrl">请求地址（例：http://xxx.com/acquireData）</param>
		/// <param name="param">请求参数（例：phone=12345678901）</param>
		/// <param name="isGzip">是否启用GZIP压缩</param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <param name="isUseCert">是否使用证书</param>
		/// <param name="timeout">请求超时时间（秒）</param>
		/// <returns></returns>
		public static HttpWebResponse HttpPostResultResponseByJSON(string postUrl, string param, bool isGzip = false, Hashtable hasHeaders = null, bool isUseCert = false, int timeout = 30)
		{
			return HttpPostResultResponse(postUrl, param, "application/json", isGzip, hasHeaders, isUseCert, timeout);
		}
		/// <summary>
		/// http post 请求方法，返回请求结果
		/// </summary>
		/// <param name="postUrl">请求地址（例：http://xxx.com/acquireData）</param>
		/// <param name="param">请求参数（例：phone=12345678901）</param>
		/// <param name="ContentType">application/x-www-form-urlencoded或application/json</param>
		/// <param name="isGzip">是否启用GZIP压缩</param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <param name="isUseCert">是否使用证书</param>
		/// <param name="timeout">请求超时时间（秒）</param>
		/// <returns></returns>
		public static string HttpPost(string postUrl, string param, string ContentType = "application/x-www-form-urlencoded", bool isGzip = false, Hashtable hasHeaders = null, bool isUseCert=false, int timeout=30)
		{
			string result = "";//返回结果
			HttpWebResponse response = null;
			try
			{
				//获取服务端返回
				response = HttpPostResultResponse(postUrl, param, ContentType, isGzip, hasHeaders, isUseCert, timeout);
				Stream responseStream = response.GetResponseStream();
				result = StreamToString(responseStream);
			}
			catch (Exception e)
			{
				throw new WxException(e.ToString());
			}
			finally
			{
				//关闭连接和流
				if (response != null)
				{
					response.Close();
				}
			}
			return result;
		}
		/// <summary>
		/// http post 请求方法，返回请求结果
		/// </summary>
		/// <param name="postUrl">请求地址（例：http://xxx.com/acquireData）</param>
		/// <param name="param">请求参数（例：phone=12345678901）</param>
		/// <param name="ContentType">application/x-www-form-urlencoded或application/json</param>
		/// <param name="isGzip">是否启用GZIP压缩</param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <returns></returns>
		public static byte[] HttpPostResultByte(string postUrl, string param, string ContentType = "application/x-www-form-urlencoded", bool isGzip = false, Hashtable hasHeaders = null)
		{
			try
			{
				byte[] bytes = Encoding.UTF8.GetBytes(param);
				byte[] byteParam;
				if (isGzip)
				{
					MemoryStream ms = new MemoryStream();
					GZipStream compressedzipStream = new GZipStream(ms, CompressionMode.Compress, true);
					compressedzipStream.Write(bytes, 0, bytes.Length);
					compressedzipStream.Close();
					byteParam = ms.ToArray();
				}
				else
				{
					byteParam = bytes;
				}
				HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
				if (hasHeaders != null)
				{
					foreach (DictionaryEntry de in hasHeaders)
					{
						request.Headers.Add(de.Key.ToString(), de.Value.ToString());
					}
				}
				request.Timeout = 30000;
				request.Method = "Post";
				request.ContentType = ContentType;
				request.ContentLength = byteParam.Length;
				Stream requestStream = request.GetRequestStream();
				requestStream.Write(byteParam, 0, byteParam.Length);
				requestStream.Close();
				HttpWebResponse response = (HttpWebResponse)request.GetResponse();
				Stream responseStream = response.GetResponseStream();
				return StreamToBytes(responseStream);
			}
			catch (Exception)
			{
				return null;
			}
		}
		/// <summary>
		/// http post 请求方法，请求参数格式为JSON，返回请求结果
		/// </summary>
		/// <param name="purl">请求地址（例：http://xxx.com/acquireData）</param>
		/// <param name="str">请求参数（例：{"name":"aaa","age":"18"}）</param>
		/// <param name="isGzip">是否启用GZIP压缩</param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <param name="isUseCert">是否使用证书</param>
		/// <param name="timeout">请求超时时间（秒）</param>
		/// <returns></returns>
		public static string HttpPostByJSON(string purl, string str, bool isGzip = false, Hashtable hasHeaders = null, bool isUseCert = false, int timeout = 30)
		{
			return HttpPost(purl, str, "application/json", isGzip, hasHeaders, isUseCert, timeout);
		}
		/// <summary>
		/// http get 请求方法，返回请求结果
		/// </summary>
		/// <param name="purl">请求地址（例：http://xxx.com/acquireData?name=aaa）</param>
		/// <param name="ContentType"></param>
		/// <param name="hasHeaders">需要添加到请求头中的键值对</param>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public static string HttpGet(string purl, string ContentType = "application/x-www-form-urlencoded", Hashtable hasHeaders = null, int timeout = 30)
		{
			System.GC.Collect();
			string result = "";//返回结果
			HttpWebRequest request = null;
			HttpWebResponse response = null;
			if (string.IsNullOrEmpty(purl))
			{
				return result;
			}
			try
			{
				request = (HttpWebRequest)WebRequest.Create(purl);
				request.Timeout = timeout * 1000;
				request.Method = "GET";
				//设置数据格式
				request.ContentType = ContentType;
				if (ContentType == "application/json")
				{
					request.Accept = ContentType;
				}
				//设置UserAgent请求头(微信支付APIV3的请求，必须带有此标头)
				string UserAgent = $"OS/{Environment.OSVersion.Platform} WxSDK/{Assembly.GetExecutingAssembly().GetName().Version}";
				request.UserAgent = UserAgent;
				//添加请求头中的键值对
				if (hasHeaders != null)
				{
					foreach (DictionaryEntry de in hasHeaders)
					{
						request.Headers.Remove(de.Key.ToString());
						request.Headers.Add(de.Key.ToString(), de.Value.ToString());
					}
				}
				//获取服务端返回
				response = (HttpWebResponse)request.GetResponse();
				Stream responseStream = response.GetResponseStream();
				result = StreamToString(responseStream);

			}
			catch (System.Threading.ThreadAbortException e)
			{
				Log.Error("HttpService", "http通信时引发的异常，具体内容如下.");
				Log.Error("异常消息: {0}", e.Message);
				System.Threading.Thread.ResetAbort();
			}
			catch (WebException e)
			{
				Log.Error("HttpService", e.ToString());
				if (e.Status == WebExceptionStatus.ProtocolError)
				{
					result = StreamToString(((HttpWebResponse)e.Response).GetResponseStream());
					Log.Error("HttpService", "Response : " + result);
					Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
					Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
					if (string.IsNullOrEmpty(result))
					{
						throw new WxException(e.ToString());
					}
				}
				else
				{
					throw new WxException(e.ToString());
				}
			}
			catch (Exception e)
			{
				Log.Error("HttpService", e.ToString());
				throw new WxException(e.ToString());
			}
			finally
			{
				//关闭连接和流
				if (response != null)
				{
					response.Close();
				}
				if (request != null)
				{
					request.Abort();
				}
			}
			return result;
		}
		/// <summary>
		/// http get 请求方法，返回请求结果
		/// </summary>
		/// <param name="purl"></param>
		/// <param name="hasHeaders"></param>
		/// <param name="timeout"></param>
		/// <returns></returns>
		public static string HttpGetByJSON(string purl, Hashtable hasHeaders = null, int timeout = 30)
		{
			return HttpGet(purl, "application/json", hasHeaders, timeout);
		}
		/// <summary>
		/// 将数据流转为byte[]
		/// </summary>
		/// <param name="stream"></param>
		/// <returns></returns>
		public static byte[] StreamToBytes(Stream stream)
		{
			List<byte> bytes = new List<byte>();
			int temp = stream.ReadByte();
			while (temp != -1)
			{
				bytes.Add((byte)temp);
				temp = stream.ReadByte();
			}
			return bytes.ToArray();
		}
		/// <summary>
		/// 将数据流转换为字符串
		/// </summary>
		/// <param name="stream"></param>
		/// <returns></returns>
		public static string StreamToString(Stream stream)
		{
			Encoding encoding = Encoding.UTF8;
			StreamReader reader = new StreamReader(stream, encoding);
			char[] buffer = new char[256];
			int length = reader.Read(buffer, 0, 256);
			StringBuilder builder = new StringBuilder("");
			while (length > 0)
			{
				string str2 = new string(buffer, 0, length);
				builder.Append(str2);
				length = reader.Read(buffer, 0, 0x100);
			}
			reader.Close();
			return builder.ToString();
		}
		#endregion

	}
}
